package com.codegen.utils;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import com.codegen.entity.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;

import freemarker.cache.NullCacheStorage;
import freemarker.core.ParseException;
import freemarker.template.Configuration;
import freemarker.template.MalformedTemplateNameException;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateNotFoundException;

/**
 * @author LZCXTECH06
 * @date 2019年8月29日
 */
@Slf4j
public class GenUtils {

    private static final String DIR_TEMPLATE = "template";
    private static List<File> TEMPLATES;
    private static Configuration CONFIG;
    private static File TMPL_DIR;

    static {
        try {
            String tempBasePath = URLDecoder.decode(GenUtils.class.getResource("/" + DIR_TEMPLATE).getPath(), "UTF-8");
            TEMPLATES = new ArrayList<File>();
            TMPL_DIR = new File(tempBasePath);
            if (TMPL_DIR.exists()) {
                appendTemplate(TMPL_DIR, TEMPLATES);
            } else {
                throw new RuntimeException("找不到目录:" + DIR_TEMPLATE);
            }

            // 第一步：创建一个Configuration对象，直接new一个对象。构造方法的参数就是freemarker对于的版本号。
            CONFIG = new Configuration(Configuration.getVersion());
            /*CONFIG.unsetCacheStorage();*/

            // 第二步：设置模板文件所在的路径。
            CONFIG.setDirectoryForTemplateLoading(TMPL_DIR);

            // 第三步：设置模板文件使用的字符集。一般就是utf-8.
            CONFIG.setDefaultEncoding("UTF-8");

            CONFIG.setCacheStorage(NullCacheStorage.INSTANCE);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void addGenFileToZip(String fileName, String content, ZipOutputStream zip) {
        try {
            zip.putNextEntry(new ZipEntry(fileName));
            IOUtils.write(content, zip, "UTF-8");
            zip.closeEntry();
        } catch (IOException e) {
            e.printStackTrace();
            throw new AppException("渲染模板失败，表名：");
        }
    }

    public static void writrToFile(String content, String filePath) {
        File file = new File(filePath);
        File fileParent = file.getParentFile();
        try {
            if (!fileParent.exists()) {
                fileParent.mkdirs();
            }
            if (!file.exists()) {
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            FileUtils.writeStringToFile(file, content, "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
            throw new AppException("渲染模板失败，表名：");
        }
    }

    private static String getFileContent(Object data, String templateName) throws IOException, TemplateException {
        CONFIG.clearTemplateCache();
        Template template = CONFIG.getTemplate(templateName);
        try (StringWriter sw = new StringWriter()) {
            template.process(data, sw);
            return sw.toString();
        } catch (IOException e) {
            log.error("渲染模板失败", e);
            throw new AppException("渲染模板失败，表名：");
        }
    }

    public static void generatorCodeToPath(TableGenSchema schema) throws IOException, TemplateException {
        Map<String, String> contentMapping = generatorCode(schema);
        String basePath = schema.getGenParam().getGenToPath();
        for (Map.Entry<String, String> entity : contentMapping.entrySet()) {
            String fileName = entity.getKey();
            String content = entity.getValue();
            String filePath = basePath + "/" + fileName;
            writrToFile(content, filePath);
        }
    }

    public static void generatorCodeToZip(TableGenSchema schema, ZipOutputStream zip) throws IOException, TemplateException {
        Map<String, String> contentMapping = generatorCode(schema);
        for (Map.Entry<String, String> entity : contentMapping.entrySet()) {
            String fileName = entity.getKey();
            String content = entity.getValue();
            addGenFileToZip(fileName, content, zip);
        }
    }

    public static Map<String, String> generatorCode(TableGenSchema schema) throws IOException, TemplateException {
        Map<String, String> tmplContentMapping = new HashMap<>();
        for (File tmplFile : TEMPLATES) {
            String templateName = tmplFile.getAbsolutePath().replace(TMPL_DIR.getPath(), "");

            if (templateName.endsWith("Enum.java.ftl")) {
                // 是枚举的模版
                List<TableField> fields = schema.getTableFields();
                for (TableField field : fields) {
                    if (field.isEnumField()) {
                        Map<String, Object> data = new HashMap<>();
                        data.put("table", schema);
                        data.put("field", field);
                        String fileName = getFileName(templateName, schema, field.getEnumClassName());
                        String content = getFileContent(data, templateName);
                        tmplContentMapping.put(fileName, content);
                        //addTempFileToZip(data, templateName, fileName, zip);
                    }
                }
            } else if (templateName.endsWith("Vo.java.ftl")) {
                // 扩展字段放入Vo表
                List<Field> extendedFields = schema.getExtendedFields();
                if (CollectionUtils.isEmpty(extendedFields)) {
                    continue;
                }
                String fileName = getFileName(templateName, schema, "");
                String content = getFileContent(schema, templateName);
                tmplContentMapping.put(fileName, content);
                //addTempFileToZip(schema, templateName, fileName, zip);
            } else {
                String fileName = getFileName(templateName, schema, "");
                String content = getFileContent(schema, templateName);
                tmplContentMapping.put(fileName, content);
                //addTempFileToZip(schema, templateName, fileName, zip);
            }
        }

        return tmplContentMapping;
    }

    private static void appendTemplate(File tplDir, List<File> templates) {
        for (File file : tplDir.listFiles()) {
            if (file.isDirectory()) {
                appendTemplate(file, templates);
            } else {
                templates.add(file);
            }
        }
    }

    /**
     * 获取文件名
     *
     * @param enumName
     */
    private static String getFileName(String template, TableGenSchema schema, String enumName) {
        String className = schema.getClassName();
        String packageName = schema.getPackageName();
        String moduleName = schema.getModuleName();
        String basePath = FilenameUtils.getPath(template) + FilenameUtils.getBaseName(template.replace(".ftl", ""));
        String extension = FilenameUtils.getExtension(template.replace(".ftl", ""));
        return basePath.replace("${className}", className).replace("${classname}", className.toLowerCase())
                .replace("${packagePath}", packageName).replace("${moduleName}", moduleName)
                .replace("${enumName}", enumName).replace(".", File.separator) + "." + extension;
    }
}
